package com.univrz.flexmachine.scxml.model
{
	

	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;

	
	
	/**
	 * 
	 * Core module only
	 * 
	 * http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation
	 * 
	 * 
	 * 
	 * 
	 * @author aldo.bucchi@gmail.com
	 * 
	 */	
	public class SCXMLCoreInterpreter extends EventDispatcher
	{
	
	
	
		private var root:StateNode;
		

		/**
		 * 
		 * Holds a reference to the currently active state nodes
		 * 
		 */
		private var configuration	:Dictionary 	= new Dictionary( );
		private var datamodel		:Object 		= {};
			
		private var continue_		:Boolean = true;

		
		private var externalQueue:Array = [];
		private var internalQueue:Array = [];



		public function SCXMLCoreInterpreter( root:StateNode )
		{

			this.root = root;
		}
	
		
		
		public function start(  ):void
		{
			
			execute( root.initial.transition.content );
			
			enterState( [ root.initial.transition ] );
			
			startEventLoop( );

		}
		
		
		
		
		private function execute( executableContent:* ):void
		{
		
			dispatchEvent( 
				new SCXMLCoreInterpreterEvent( SCXMLCoreInterpreterEvent.EXECUTE, executableContent )
					);
		
		}
		

		private function startEventLoop( ):void
		{
		
			var enabledTransitions:Array = selectTransitions( null );
			
			if (  enabledTransitions.length==0  &&  internalQueue.length!=0  )
			{
				enabledTransitions = selectTransitions( internalQueue.shift( ) );
			}     
		        
		         
			if (  enabledTransitions.length==0  &&  internalQueue.length!=0  )
			{
//				THIS IS THE TRICKY PART
//				if ( continue_ ):
//				ee = externalQueue.dequeue( );
//				
//				datamodel.assignID("event",ee)
//				enabledTransitions = selectTransitions(ee)
//				else:
//				break
			}
		        

			if ( enabledTransitions.length!=0 )
				microstep( enabledTransitions );
		
		}		
		
		


		
		
		private function loop( ):void
		{
		
		
			
		
		
		
		
		}




		private function getActiveStates( ):Array
		{
			return root.descendants.filter( 
				function( state:StateNode, index:int, arr:Array ):Boolean{
					return state.isActive;
				}
			 );
		}


		private function getActiveAtomicStates( ):Array
		{
			return root.descendants.filter( 
				function( state:*, index:int, arr:Array ):Boolean{
					return state.isActive && state.isAtomic;
				}
			 );		
		}
		




		/**
		 * 
		 * 
		 * 
		 * @param event
		 * @return 
		 * 
		 */
		private function selectTransitions( event:String ):Array
		{
			
			var enabledTransitions		:Array = new Array( );
			var atomicStates			:Array = getActiveAtomicStates( );
			var state:StateNode;
			var s:StateNode;
			var t:TransitionEdge;
		   
			for each ( state in atomicStates )
			{
				if ( ! state.isPreempted( enabledTransitions ) )
				{
		      	
		         loop: for each ( s in s.ancestorsIncludingThis )
		         	{
			            for each ( t in s.transitions )
			            {
							if ( t.matchesEvent( event ) && t.canFire )
							{
								enabledTransitions.push( t );
								break loop;
							}
			            }
		         	}
				}
			}
			return enabledTransitions;
		
		}
		
		
		
		

		private function microstep( enabledTransitions:Array ):void
		{
		   exitStates( enabledTransitions );
		   executeTransitionContent( enabledTransitions );
		   enterStates( enabledTransitions );
		}
		


		private function exitStates( enabledTransitions:Array ):void
		{
			
			
			var statesToExit:Array = [ ],
				t:TransitionEdge ,
				s:StateNode ;
		   
			for each ( t in enabledTransitions )
			{
				if ( t.targets.length > 0 )
				{
					var lca:Node = t.sourceAndTargetsLCA;
					for each ( s in toList( configuration ) )
						if ( s.isDescendantOf( lca ) )
							statesToExit.push( s );
				}
			}
		   

		   // statesToExit = statesToExit.sort( exitOrder );
		   

		   for each ( s in statesToExit )
		   {
//		      for each ( h in s.history )
//		      {
////		         f = (h.attribute('type') == "deep") ?
////		             lambda(s0): isAtomicState(s0) && isDescendant(s0,s) :
////		             lambda(s0): s0.parent( ) == s
////		             
////		         historyValue[h.attribute('id')] = toList( configuration ).filter( f );
//		      }
		   }


		   
//		   for ( s in statesToExit )
//		   {
//		      for content in s.onexit
//		      	{
//		         executeContent(content)
//		       }
//		      for inv in s.invoke
//		      {
//		         cancelInvoke(inv)
//		      }
//		      delete configuration[s];
//		   }  

		
		}






		private function executeTransitions( enabledTransitions:Array ):void
		{
		   for each ( var t:TransitionEdge in enabledTransitions )
		   {
		      execute( t.content );
		   }
		}


		
		
		private function enterStates( enabledTransitions:Array ):void
		{
//			var t:XML, s:XML, LCA:XML;
//			
//			var statesToEnter:Array = new Array( );
//			var statesForDefaultEntry:Array = new Array( );
//						
//						
//		   for ( t in enabledTransitions )
//		   {
//		      if ( t.attribute('target') != null )
//		      {
//		         LCA = findLCA( [ t.parent( ) ] .append( getTargetStates( t ) ) );
//		         
//		         for ( s in getTargetStates( t ) )
//		         {
//		            if ( isHistoryState( s ) )
//		            {
//		               if ( historyValue[s.attribute('id')] != null )
//		               {
//		                  for ( s0 in historyValue[s.attribute('id')] )
//		                  {
//		                     addStatesToEnter(s0,LCA,statesToEnter,statesForDefaultEntry)
//		                  }
//		               }
//		               else
//		               {
//		                  for ( s0 in getTargetStates( s.transition ) )
//		                  {
//		                     addStatesToEnter(s0,LCA,statesToEnter,statesForDefaultEntry)
//		                  }
//		               }
//		            }
//		            else
//		            {
//		               addStatesToEnter(s,LCA,statesToEnter,statesForDefaultEntry);
//		            }
//		         }
//		      for ( anc in getProperAncestors(stateList.head(),null) )
//		      {
//				statesToEnter = statesToEnter.toList().sort(enterOrder)
//		      }
//		   
//		   for s in statesToEnter:
//		      configuration.add(s)
//		      for inv in s.invoke:
//		         sessionID = executeInvoke(inv)
//		         datamodel.assignValue(inv.attribute('ID'),sessionID)
//		      for content in s.onentry:
//		         executeContent(content)
//		      if (statesForDefaultEntry.member(s)):
//		         executeContent(s.initial.transition.children())
//		      if (isFinalState(s)):
//		         parent = s.parent()
//		         grandparent = parent.parent()
//		         internalQueue.enqueue(parent.attribute('id') + ".Done")
//		         if (isParallelState(grandparent))
//		         {
//		            if (getChildStates(grandparent).every(isInFinalState))
//		            {
//		               internalQueue.enqueue(grandparent.attribute('id') + ".Done")
//		            }
//		         }
//		   for ( s in configuration.toList() )
//		   {
//		      if (isFinalState(s) && isScxmlState(s.parent()))
//		      {
//		         exitInterpreter()
//		      }
//		   }
		}
		
		
		
		
		private function isAtomicState( s:XML ):Boolean
		{
		
			// TODO
			return false;
		}
		
		
		
		
		private function executeTransitionContent( v:* ):void
		{
		
		}
		
		
		private function enterState( v:* ):void
		{
		
		}
		
		
		public function exitInterpreter( ):void
		{
   			continue_ = false;
		}
		
		
		
		
		
		
		////////////////////////////////////////////////////
		// utils
		////////////////////////////////////////////////////
		
		
		private function toList( dict:Dictionary ):Array
		{
			var arr:Array = [ ];
			for ( var k:* in dict )
				arr.push( k );
			return arr;
		}
		
		
		

	}
	
}

